/*
 * @author Chris
 * @since 2023/1/20 16:03
 *
 * @brief    The project will realize addition calculator. input num key, and use Up key to plus,
 *           and Enter key to get result, and ESC key to cancel.
 *
 * @note     {@link KeyDriver()} check key action and call action function - {@link KeyAction()}
 *           {@link KeyAction()} do related action through key action.
 *           {@link KeyScan()} scan key matrix.
 *           {@link LedScan()} scan led nixie tube.
 *           {@link ShowNumber()} show result at led nixie tube.
 *           {@link InterruptTimer0()} interrupt service function, call {@link KeyScan()} and {@link LedScan()}.
 *
 * @warning  Do not to ...
 *
 * Change Logs:
 *   Date           Author       Notes
 *   2023-01-20     Chris        the first version
 */

#include "reg51.h"

#define ADDR3 P1_3
#define ENLED P1_4

#define KEY_IN_1 P2_4
#define KEY_IN_2 P2_5
#define KEY_IN_3 P2_6
#define KEY_IN_4 P2_7

// 数码管显示真值表
unsigned char __code ledTruthTable[] = {
        0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
        0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};

// 数码管显示缓冲区
unsigned char ledBuf[6] = {
        0xFF,
        0xFF,
        0xFF,
        0xFF,
        0xFF,
        0xFF
};
// 按键矩阵编号与标准键盘映射
unsigned char __code keyMap[4][4] = {
        {0x31, 0x32, 0x33, 0x26},   // 1、2、3、上
        {0x34, 0x35, 0x36, 0x25},   // 4、5、6、左
        {0x37, 0x38, 0x39, 0x28},   // 7、8、9、下
        {0x30, 0x1B, 0x0D, 0x27},   // 0、ESC、ENTER、右
};

// 记录按键的输入状态
unsigned char keyState[4][4] = {
        {1, 1, 1, 1},
        {1, 1, 1, 1},
        {1, 1, 1, 1},
        {1, 1, 1, 1}
};

void KeyDriver();

int main() {
    ENLED = 0;
    ADDR3 = 1;

    EA = 1;      // 使能总中断
    TMOD = 0x01; // 设置 T0 为模式 1
    TH0 = 0xFC;  // 为 T0 赋初值，定时 2ms
    TL0 = 0x66;

    ET0 = 1;     // 使能 T0 中断
    TR0 = 1;     // 启动 T0

    while (1) {
        KeyDriver();
    }

}

void ShowNumber(unsigned long num) {
    unsigned char buf[6];

    for (int i = 0; i < 6; ++i) {
        buf[i] = num % 10;
        num /= 10;
    }

    __sbit flag = 1;
    for (int i = 5; i >= 0; i--) {
        if (buf[i] == 0 && flag == 1) {
            ledBuf[i] = 0xFF;
        } else {
            flag = 0;
            ledBuf[i] = buf[i];
        }
    }
}

void KeyAction(unsigned char key) {
    static unsigned long result = 0;
    static unsigned long currValue = 0;

    if (key >= 0x30 && key <= 0x39) {
        // 数字键被按下
        currValue = currValue * 10 + (key - 0x30);
        ShowNumber(currValue);
    } else if (key == 0x26) {
        // “+”
        result += currValue;
        currValue = 0;
        ShowNumber(result);
    } else if (key == 0x0D) {
        // Enter 按下
        result += currValue;
        currValue = 0;
        ShowNumber(result);
    } else if (key == 0x1b) {
        result = 0;
        currValue = 0;
        ShowNumber(0);
    } else {}

}

void KeyDriver() {
    static unsigned char keyBackup[4][4] = {
            {1, 1, 1, 1},
            {1, 1, 1, 1},
            {1, 1, 1, 1},
            {1, 1, 1, 1}
    };

    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            if (keyState[i][j] != keyBackup[i][j]) {
                if (keyBackup[i][j] == 1) {
                    KeyAction(keyMap[i][j]);
                }
                keyBackup[i][j] = keyState[i][j];
            }
        }
    }
}

void KeyScan() {
    // 当前扫描的行
    static unsigned char row = 0;
    // 按键状态记录
    static unsigned char keyBuf[4][4] = {
            {0xFF, 0xFF, 0xFF, 0xFF},
            {0xFF, 0xFF, 0xFF, 0xFF},
            {0xFF, 0xFF, 0xFF, 0xFF},
            {0xFF, 0xFF, 0xFF, 0xFF},
    };

    keyBuf[row][0] = (keyBuf[row][0] << 1) | KEY_IN_1;
    keyBuf[row][1] = (keyBuf[row][1] << 1) | KEY_IN_2;
    keyBuf[row][2] = (keyBuf[row][2] << 1) | KEY_IN_3;
    keyBuf[row][3] = (keyBuf[row][3] << 1) | KEY_IN_4;

    for (int i = 0; i < 4; ++i) {
        if ((keyBuf[row][i] & 0x0F) == 0x0F) {
            keyState[row][i] = 1;
        } else if ((keyBuf[row][i] & 0x0F) == 0x00) {
            keyState[row][i] = 0;
        } else {}
    }

    row++;
    row = row & 0x03;   // 逢 4 归 0

    P2 = P2 & 0xF0 | (~(1 << (3 - row)));
}

void LedScan() {
    static unsigned char index = 0;

    P0 = 0xFF;
    P1 = P1 & 0xF8 | index;
    P0 = ledTruthTable[ledBuf[index]];
    index++;
    index = index > 5 ? 0 : index;
}

/**
 * 定时器 T0 中断服务函数
 */
void InterruptTimer0() __interrupt(1) {
    TH0 = 0xFC; // 为 T0 赋初值，定时 2ms
    TL0 = 0x66;

    KeyScan();
    LedScan();
}